using Abp.Collections.Extensions;
using Abp.Dependency;
using Abp.Extensions;
using Abp.ObjectMapping;
using Abp.Text;
using Mt.Site.Application.FileFinder.Dto;
using Mt.Site.Application.FileFinder.Dto.Upload;
using Mt.Site.Core.Base.BlobStoring;
using Mt.Site.Core.Base.Utils.FileIO.Upload;
using Mt.Site.Core.Organizations.UserCache;
using Mt.Site.Core.Storage.File_;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Mt.Site.Application.FileFinder
{
    public class FileLibraryUploadHelper : ITransientDependency
    {

        public async Task<FileLibrary> SaveNewFileInfoToLibraryAsync(
         FileSaveParams saveParams, string fileName, long fileSize, string fileHash, string dimensions, Guid? directoryId, IFileLibraryManager fileLibraryManager)
        {
            var fileInfo = new UploadFileInfo
            {
                FileUrl = saveParams.SavePath,
                FileName = fileName,
                FileSize = fileSize,
                Dimensions = dimensions,
                Sha1 = fileHash,
            };

            var fileEntity = saveParams.FileEntity;
            if (fileEntity != null)
            {
                await fileLibraryManager.UpdateFileInfoToDbByFileUrlAsync(fileEntity, fileInfo, saveParams.ExNameWithoutPoint);
            }
            else
            {
                fileEntity = await fileLibraryManager.AddToFileLibraryAsync(
                    fileInfo, saveParams.ExNameWithoutPoint, directoryId);
            }

            return fileEntity;
        }

        public async Task<FileSaveParams> CheckFileSaveParams(
            string fileName, long replaceFileId, IBlobContainer container, IFileLibraryManager fileLibraryManager, string exName)
        {
            var newFileName = RemoveIllegalCharacter(fileName);

            var exNameWithoutPoint = exName.Substring(1);
            string savePath = null;
            FileLibrary fileEntity = null;
            if (replaceFileId > 0)
            {
                fileEntity = await fileLibraryManager.GetFileLibraryOrNullAsync(replaceFileId);
                if (fileEntity != null)
                {
                    savePath = fileEntity.FileUrl;
                }
            }

            var isImage = UpFileInfoExtension.ExNameCamelCaseIsImage(exNameWithoutPoint);

            if (savePath.IsNullOrWhiteSpace())
            {
                var saveDir = "Ckeditor";

                if (isImage || exNameWithoutPoint == "gif" || exNameWithoutPoint == "svg")
                {
                    saveDir = UpFileInfoExtension.JoinPrefixWithFileName(saveDir, "Images");
                }
                else
                {
                    saveDir = UpFileInfoExtension.JoinPrefixWithFileName(saveDir, "Files");
                }
                var date = DateTime.Now.ToString("yyyy-MM-dd");
                saveDir = UpFileInfoExtension.JoinPrefixWithFileName(saveDir, date);

                newFileName = await CheckFileName(saveDir, newFileName, exName, container);
                savePath = UpFileInfoExtension.JoinPrefixWithFileName(saveDir, newFileName);
            }

            return new FileSaveParams
            {
                SavePath = savePath,
                ExName = exName,
                IsImage = isImage,
                ExNameWithoutPoint = exNameWithoutPoint,
                NewFileName = newFileName,
                FileEntity = fileEntity
            };
        }

        public async Task<CheckFileIsExistResult> CheckFileIsExist(IFileLibraryManager fileLibraryManager, string sha1)
        {
            var fileLibrary = await fileLibraryManager.IsExistInLibrary(sha1);
            if (fileLibrary != null)
            {
                return new CheckFileIsExistResult
                {
                    UploadFileInfo = new UploadFileInfo
                    {
                        FileName = fileLibrary.FileName,
                        FileUrl = fileLibrary.FileUrl,
                        FileSize = fileLibrary.Size,
                        Sha1 = sha1,
                        Dimensions = fileLibrary.Dimensions
                    },
                    SourceName = FileScoureName.FromLibrary,
                    ExName = fileLibrary.ExName
                };
            }

            var fileObject = await fileLibraryManager.IsExistInFileObject(sha1);
            if (fileObject != null)
            {
                return new CheckFileIsExistResult
                {
                    UploadFileInfo = new UploadFileInfo
                    {
                        FileName = fileObject.FileName,
                        FileUrl = fileObject.FileUrl,
                        FileSize = fileObject.FileLength,
                        Sha1 = sha1,
                        //TODO:后面改字段名
                        Dimensions = fileObject.Size
                    },
                    SourceName = FileScoureName.FromFileObject,
                    ExName = fileObject.exName
                };
            }

            return null;
        }

        public async Task<FileLibrary> SaveExistFileToLibraryAsync(
            CheckFileIsExistResult file, long replaceFileId, Guid? directoryId, string fileName,
            IFileLibraryManager fileLibraryManager, IBlobContainer container)
        {
            if (file == null)
            {
                return null;
            }

            file.UploadFileInfo.FileName = fileName;


            if (replaceFileId > 0)
            {
                var fileEntity = await fileLibraryManager.GetFileLibraryOrNullAsync(replaceFileId);
                if (fileEntity != null && fileEntity.FileUrl != file.UploadFileInfo.FileUrl)
                {
                    await container.CopyAsync(file.UploadFileInfo.FileUrl, fileEntity.FileUrl, true);

                    await fileLibraryManager.UpdateFileInfoToDbByFileUrlAsync(fileEntity, file.UploadFileInfo, file.ExName);
                }

                return fileEntity;
            }
            else
            {
                var fileEntity = await fileLibraryManager.AddToFileLibraryAsync(
                    file.UploadFileInfo, file.ExName, directoryId);

                return fileEntity;
            }
        }

        public async Task<LibraryFileItem> FileLibraryToFileItemAsync(
             [NotNull] List<Directories> directoriesData, long userId, FileLibrary t,
             IObjectMapper objectMapper, IUserAndUnitFinder userAndUnitFinder, IFileLibraryManager fileLibraryManager)
        {
            var item = objectMapper.Map<LibraryFileItem>(t);

            if (t.CreatorUserId != null)
            {
                item.CreatorUser = (await userAndUnitFinder.GetUserInfoAsync(t.CreatorUserId.Value))?.UserName;
            }
            if (t.LastModifierUserId != null)
            {
                item.LastModifierUser = (await userAndUnitFinder.GetUserInfoAsync(t.LastModifierUserId.Value))?.UserName;
            }

            string itemCode = directoriesData.FirstOrDefault(tt => tt.Id == item.DirectoryId)?.Code;

            if (itemCode != null)
            {
                var codes = itemCode.Split(".", StringSplitOptions.RemoveEmptyEntries);
                item.PathItems = new List<PathItem>();
                var pre = "";
                for (int i = 0; i < codes.Length; i++)
                {
                    var code = codes[i];
                    if (!pre.IsNullOrEmpty())
                    {
                        pre = pre + ".";
                    }
                    pre = pre + code;

                    var dir = directoriesData.FirstOrDefault(tt => tt.Code == pre);
                    if (dir != null)
                    {
                        item.PathItems.Add(new PathItem
                        {
                            DisplayName = dir.DisplayName,
                            Id = dir.Id,
                            Code = dir.Code,
                            IsDefault = dir.IsDefault,
                            ReadOnly = !fileLibraryManager.IsCanEdit(dir, userId)
                        });
                    }
                    else
                    {
                        break;
                    }
                }
            }

            return item;
        }

        private async Task<string> CheckFileName(string pathPrefix, string newFileName, string exName, IBlobContainer container)
        {
            string filePath = UpFileInfoExtension.JoinPrefixWithFileName(pathPrefix, newFileName);

            bool isExist = await container.ExistsAsync(filePath);

            while (isExist)
            {
                //重命名
                newFileName = GetNewFileName(newFileName, exName);
                filePath = UpFileInfoExtension.JoinPrefixWithFileName(pathPrefix, newFileName);
                isExist = await container.ExistsAsync(filePath);
            }

            return newFileName;
        }



        private string GetNewFileName(string fileName, string exName)
        {
            var result = new FormattedStringValueExtracter().Extract(fileName, "{name}({num})" + exName);
            if (result.IsMatch)
            {
                int num;
                if (int.TryParse(result.Matches[1].Value, out num))
                {
                    return $"{result.Matches[0].Value}({num + 1}){exName}";
                }
            }

            return $"{fileName.Substring(0, fileName.LastIndexOf("."))}(1){exName}";
        }

        private string RemoveIllegalCharacter(string newFileName)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in newFileName)
            {
                if (c == '?' || c == '&' || c == '#' || c == 32 || c == 12288)
                {
                    sb.Append('_');
                }
                else
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
    }
}
